package compiler.mine;

import compiler.Nfa;
import compiler.stdin.Input;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Stack;

/**
 * Created by szj on 2017/2/3.
 */
public class Interpreter {
    private Input input;
    private Nfa start;
    public boolean debug;

    public Interpreter(){

    }

    public Interpreter(Input input, Nfa start) {
        this.input = input;
        this.start = start;
    }

    public void interpret() {
        //从控制台读入要解读的字符串
        System.out.println("Input string: ");
        input.ii_newFile(null);
        input.ii_advance();
        input.ii_pushback(1);

        Set<Nfa> nfas = new HashSet<>();
        nfas.add(start);
        nfas = eClosure(nfas);
        String inputStr = "";
        char c;
        while ((c = (char) input.ii_advance()) != input.EOF) {
            nfas = eClosure(nfas);
            inputStr += c;
            nfas = move(nfas, c);
            if (nfas == null) {
                throw new RuntimeException("无法识别的字符串");
            }
            if (hasAcceptStatus(nfas)) {
                System.out.println("The Nfa Machine can recognize string: " + inputStr);
            }
        }
    }

    private boolean hasAcceptStatus(Set<Nfa> nfas) {
        String acceptedStatement = "Accept State: ";
        for (Nfa nfa : nfas) {
            if(nfa.next == null && nfa.next2 == null){
                System.out.println(acceptedStatement + nfa.getStateNum());
                return true;
            }
        }
        return false;
    }

    public Set<Nfa> move(Set<Nfa> input, char c) {
        Set<Nfa> outSet = new HashSet<>();
        if (input == null || input.isEmpty()) {
            System.out.println("input is null");
            return null;
        }

        Iterator<Nfa> iterator = input.iterator();
        while (iterator.hasNext()) {
            Nfa nfa = iterator.next();
            if (nfa.getEdge() == c || nfa.inputSet.contains(c)) {
                outSet.add(nfa.next);
            }
        }

        System.out.print("move({ " + strFromNfaSet(input) + " }, '" + c + "')= ");
        System.out.println("{ " + strFromNfaSet(outSet) + " }");

        return outSet;
    }

    public Set<Nfa> eClosure(Set<Nfa> input) {
        if (input == null || input.isEmpty()) {
            System.out.println("input is null");
            return null;
        }
        /*
         * 计算input集合中nfa节点所对应的ε闭包，
    	 * 并将闭包的节点加入到input中
    	 */
        System.out.print("ε-Closure( " + strFromNfaSet(input) + " ) = ");

        Stack<Nfa> stack = new Stack<>();
        Iterator<Nfa> iterator = input.iterator();
        while (iterator.hasNext()) {
            stack.push(iterator.next());
        }
        while (!stack.isEmpty()) {
            Nfa nfa = stack.pop();
            if (nfa.getEdge() == Nfa.EPSILON
                    && nfa.next != null
                    && !input.contains(nfa.next)) {

                input.add(nfa.next);
                stack.push(nfa.next);
            }
            if (nfa.getEdge() == Nfa.EPSILON
                    && nfa.next2 != null
                    && !input.contains(nfa.next2)) {

                input.add(nfa.next2);
                stack.push(nfa.next2);
            }
        }

        System.out.println("{ " + strFromNfaSet(input) + " }");

        return input;
    }

    private String strFromNfaSet(Set<Nfa> input) {
        String s = "";
        Iterator it = input.iterator();
        while (it.hasNext()) {
            s += ((Nfa) it.next()).getStateNum();
            if (it.hasNext()) {
                s += ",";
            }
        }

        return s;
    }
}
